package com.shiro.shiroforadmin.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.shiro.shiroforadmin.config.TokenConfig;
import com.shiro.shiroforadmin.dao.AdminMapper;
import com.shiro.shiroforadmin.pojo.Admin;
import com.shiro.shiroforadmin.pojo.AdminExample;
import com.shiro.shiroforadmin.pojo.Role;
import com.shiro.shiroforadmin.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

public class AuthenticationInterceptor implements HandlerInterceptor {


    @Autowired
    private AdminService adminService;

    @Autowired
    private AdminMapper adminMapper;


    @Override
    //prehandle函数会在Controller里的函数被调用之前提前调用
    //所以在这里可以达到提前拦截的效果
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 从 http 请求头中取出 token
        String token = request.getHeader("token");
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();//获取到被注解的函数


        //检查函数是否有@PassToken注解，有则直接跳过认证
        if (method.isAnnotationPresent(TokenConfig.PassToken.class)) {
            TokenConfig.PassToken passToken = method.getAnnotation(TokenConfig.PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        //检查函数是否有UserLoginToken注解, 有则执行验证, 无则执行return true
        if (method.isAnnotationPresent(TokenConfig.UserLoginToken.class)) {
            TokenConfig.UserLoginToken userLoginToken = method.getAnnotation(TokenConfig.UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("当前无token信息，请重新登录!");
                }
                // 获取 token 中的 username
                String username;
                try {
                    username = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("Token获取失败或有误!");
                }

                //通过token中的username来验证是否存在
                Admin adminPojo = new Admin();
                adminPojo.setUsername(username);

                AdminExample exp = new AdminExample();
                AdminExample.Criteria cri = exp.createCriteria();
                cri.andUsernameEqualTo(username);
                List<Admin> admin = adminMapper.selectByExample(exp);//获得管理端用户信息

                if (ObjectUtils.isEmpty(admin)) {
                    throw new RuntimeException("当前用户名不存在，请重新登录!");
                }

                // 通过token中的password与最新查询到password对比 来验证token的正确性
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(admin.get(0).getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("Token获取失败或已过期!");
                }

                //验证操作权限
                TokenConfig.UserLoginTokenAndPerm ultap = method.getAnnotation(TokenConfig.UserLoginTokenAndPerm.class);
                List<String> roles = Arrays.asList(ultap.role());
                String perm = ultap.perm();

                if (roles.size() == 0) {
                    boolean hasPerm = verifyPerm(admin.get(0).getId(), perm);
                    if (!hasPerm) {
                        throw new RuntimeException("当前接口需要拥有权限：" + perm);
                    }
                } else {
                    boolean hasRolesAndPerm = verifyRolesAndPerm(admin.get(0).getId(), roles, perm);
                    if (!hasRolesAndPerm) {
                        throw new RuntimeException("当前接口仅角色：" + roles + "可访问, 且需要拥有权限：" + perm);
                    }
                }
                return true;
            }
        }
        return true;
    }

    public boolean verifyPerm(Integer adminId, String perm) {
        List<String> currentPerm = adminService.selectPermsById(adminId);
//        System.err.println(currentPermission);
        if (!currentPerm.contains(perm)) {
            return false;
        }
        return true;
    }


    public boolean verifyRolesAndPerm(Integer adminId, List<String> roles, String perm) {

//        System.out.println("执行了权限认证：");
//        System.out.println(adminId);
//        System.out.println(roles);
//        System.out.println(perm);

        List<Role> currentRoleList = adminService.selectRolesByUser(adminId);
        if (currentRoleList.size() == 0){
            return false;
        }
        Role currentRole = currentRoleList.get(0);
//        System.err.println(currentRole.getName());
        if (!roles.contains(currentRole.getName())) {
            return false;
        }

        List<String> currentPerm = adminService.selectPermsById(adminId);
//        System.err.println(currentPermission);
        if (!currentPerm.contains(perm)) {
            return false;
        }
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object o, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object o, Exception e) throws Exception {
    }
}
